﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using SharedGenomics.Core;
using SharedGenomics.Annotations;
using SharedGenomics.Workbench.Annotations;

namespace SharedGenomics.Workbench.Tests.Annotations
{
    [TestClass]
    public class AnnotationRequestStatusNotifierTests
    {
        private AnnotationRequestStatusNotifier notifier;
        private TestAnnotationRequestStatusNotifiable testNotifiable;

        [TestInitialize]
        public void Setup()
        {
            this.notifier = new AnnotationRequestStatusNotifier();
            this.testNotifiable = new TestAnnotationRequestStatusNotifiable { AnnotationTarget = "rs001" };
        }

        [TestMethod]
        public void ShouldNotifySubscriber()
        {
            AnnotationRequestResult result = new SucceededAnnotationRequestResult(AnnotationKey.GoogleSearch, "rs001", new GoogleSearchAnnotation("rs001"), null);

            this.notifier.AddNotificationFor(this.testNotifiable);
            this.notifier.RequestCompleted(result);

            Assert.AreEqual(result, this.testNotifiable.LastResult);
        }

        [TestMethod]
        public void ShouldNotNotifyIfTargetDifferent()
        {
            this.notifier.AddNotificationFor(this.testNotifiable);
            this.notifier.RequestCompleted(new FailedAnnotationRequestResult(AnnotationKey.GoogleSearch, "rs1111"));

            Assert.IsNull(this.testNotifiable.LastResult);
        }

        [TestMethod]
        public void ShouldNotNotifyIfSubscribersCleared()
        {
            this.notifier.AddNotificationFor(this.testNotifiable);
            this.notifier.ClearNotifications();

            AnnotationRequestResult result = new SucceededAnnotationRequestResult(AnnotationKey.GoogleSearch, "rs001", new GoogleSearchAnnotation("rs001"), null);
            this.notifier.RequestCompleted(result);

            Assert.IsNull(this.testNotifiable.LastResult);
        }

        [TestMethod]
        public void ShouldAllowConcurrentUpdatesAndNotifications()
        {
            ParameterizedThreadStart threadFunc = si =>
            {
                int startIndex = (int)si;
                var snpNames = Enumerable.Range(startIndex, 1000).Select(i => "rs" + i);

                //add notifiables
                snpNames.ForEach(snp => this.notifier.AddNotificationFor(new TestAnnotationRequestStatusNotifiable { AnnotationTarget = snp }));

                //notify
                snpNames.ForEach(snp => this.notifier.RequestCompleted(new SucceededAnnotationRequestResult(AnnotationKey.GoogleSearch, snp, new GoogleSearchAnnotation(snp), null)));
            };

            //create 10 threads each of which will register 1000 listeners and then notify an annotation request for each of them
            var threads = 10.Of(i => new Thread(threadFunc)).ToList();
            Enumerable.Range(0, 10).ForEach(i => threads[i].Start(i));
            threads.ForEach(t => t.Join());
        }

        #region TestAnnotationRequestStatusNotifiable

        private class TestAnnotationRequestStatusNotifiable : IAnnotationRequestStatusNotifiable
        {
            public AnnotationRequestResult LastResult { get; private set; }
            public object AnnotationTarget { get; set; }

            public void Notify(AnnotationRequestResult result)
            {
                this.LastResult = result;
            }
        }

        #endregion
    }
}
